// 自己实现一个promise 遵循Promise A+规范（https://promisesaplus.com/）

// 三种状态
const PENDING = 'PENDING'; // 等待态
const FULFILLED = 'FULFILLED'; // 成功态
const REJECTED = 'REJECTED'; // 失败态

class Promise {
  constructor(executor) {
    this.status = PENDING; // 默认是等待态
    this.value = ''; // 成功的原因
    this.reason = ''; // 失败的原因
    this.onResolvedCallbacks = []; // 存放成功的回调的队列
    this.onRejectedCallbacks = []; // 存放失败的回调的队列
    const resolve = (value) => {
      // 这个value可能是一个promise，要继续then递归解析，直到是普通值为止
      if (value instanceof Promise) {
        return value.then(resolve, reject);
      }
      if (this.status === PENDING) {
        // 只有当前是等待状态才能变成成功态
        this.status = FULFILLED;
        this.value = value;
        // 订阅，执行所有成功的回调
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        // 只有当前是等待状态才能变成失败态
        this.status = REJECTED;
        this.reason = reason;
        // 订阅，执行所有失败的回调
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };
    // 捕获一下异常
    try {
      executor(resolve, reject);
    } catch (e) {
      // 出错了就走reject
      reject(e);
    }
  }
  /**
   *
   * @param {成功的回调} onfulfilled
   * @param {失败的回调} onrejected
   * return 返回一个新的promise
   */
  then(onfulfilled, onrejected) {
    // 判断这个两个回调是否是传递了，如果没传要给个默认的
    onfulfilled =
      typeof onfulfilled === 'function' ? onfulfilled : (value) => value;
    onrejected =
      typeof onrejected === 'function'
        ? onrejected
        : (reason) => {
            throw reason;
          };
    const promise2 = new Promise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // 当成功的时候调用成功的回调
        // 捕获一下then里面的回调，如果错误，直接走promise2的reject

        // 使用定时器，是因为我们要确保resolvePromise能拿到promise2这个实例
        setTimeout(() => {
          try {
            let x = onfulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status === REJECTED) {
        // 当失败的时候调用成功的回调

        setTimeout(() => {
          try {
            let x = onrejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        // 等待态，分别存放成功的回调和失败的回调，实际上就是发布
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onfulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onrejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return promise2;
  }
  /**
   * 返回一个promise，没有参数，无论如何都会执行，作为中间处理，也是一个then
   * 如果上一个是成功状态，
   */
  finally(callback) {
    return this.then(
      (data) => {
        // 函数内部执行，如果是promise就等待结果
        return Promise.resolve(callback()).then(() => data);
      },
      (err) => {
        return Promise.resolve(callback()).then(() => {
          throw err;
        });
      }
    );
  }
  /**
   * 一个特殊的then，只有失败的回调
   * @param {失败的回调} errCallback
   */
  catch(errCallback) {
    return this.then(null, errCallback);
  }
  static resolve(val) {
    return new Promise(function (resolve, reject) {
      // 这里有可能val是一个promise
      resolve(val);
    });
  }
  static reject(reason) {
    return new Promise(function (resolve, reject) {
      reject(reason);
    });
  }
  // 返回的是所有的结果数组
  static allSettled(values) {
    return new Promise(function (resolve, reject) {
      let res = [],
        count = 0;
      function setData(key, value) {
        res[key] = value;
        // 计数，当长度相同的时候，则表明数组全部循环结束
        if (++count === values.length) {
          resolve(res);
        }
      }
      for (let i = 0; i < values.length; i++) {
        let current = values[i];
        if (current && typeof current.then === 'function') {
          // 是promise
          current.then(
            (value) => {
              setData(i, { status: 'fulfilled', value });
            },
            (reason) => {
              setData(i, { status: 'rejected', reason });
            }
          );
        } else {
          // 普通值
          setData(i, { status: 'fulfilled', value: current });
        }
      }
    });
  }
  /**
   * 返回的是一个promise，当一个失败则返回失败，所有成功才返回成功
   * @param {一个数组，数组里面可以是promise，简单值} values
   */
  static all(values) {
    return new Promise(function (resolve, reject) {
      if (!values.length) {
        resolve([]);
        return;
      }
      let res = [],
        count = 0;
      function setData(key, value) {
        res[key] = value;
        // 计数，当长度相同的时候，则表明数组全部循环结束
        if (++count === values.length) {
          resolve(res);
        }
      }
      for (let i = 0; i < values.length; i++) {
        let current = values[i];
        if (current && typeof current.then === 'function') {
          // 是promise
          current.then(
            (data) => {
              // 成功了才计数
              setData(i, data);
            },
            reject // 如果异常了就直接调reject
          );
        } else {
          // 普通值
          setData(i, current);
        }
      }
    });
  }
  /**
   * 当有一个成功的时候就立即返回这个成功值，有一个失败就立即返回失败
   * @param {一个数组，数组里面可以是promise，简单值} values
   */
  static race(values) {
    return new Promise(function (resolve, reject) {
      for (let i = 0; i < values.length; i++) {
        let current = values[i];
        if (current && typeof current.then === 'function') {
          // 判断是promise
          current.then(resolve, reject);
        } else {
          // 普通值
          resolve(current);
        }
      }
    });
  }
}
/**
 * 处理then方法的回调,判断返回值和promise2的关系
 * @param {返回的新的promise} promise2
 * @param {then方法返回的结果} x
 * @param {成功的方法} resolve
 * @param {失败的方法} reject
 */
function resolvePromise(promise2, x, resolve, reject) {
  // 如果x和promise2是同一个引用，要抛出异常
  // 注释① =============
  if (x === promise2) {
    return reject(new TypeError('循环引用了。。。'));
  }
  let called = false; // 这个变量是为了防止既走成功又走失败，只能走一个
  // 如果x是promise，则要看这个promise的执行结果
  if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      // 有可能取x.then的值时会抛异常，所以要捕获一下
      // 注释② =============
      let then = x.then;
      if (typeof then === 'function') {
        // then是函数，如果x是promise，则采用它的状态
        // 这里不能用 x.then，因为再取一次还是有可能报错，所以用上面已经取好的then变量
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            // 有可能y也是一个promise，则递归resolvePromise，直到返回的是一个普通值
            // 注释③ =============
            resolvePromise(promise2, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        // 普通对象
        resolve(x);
      }
    } catch (e) {
      // 取x.then可能报错
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    // 是普通值，则调用resolve把这个结果传递到下一个then的成功回调
    resolve(x);
  }
}

// 测试
Promise.defer = Promise.deferred = function () {
  let dfd = {};
  dfd.promise = new Promise(function (resolve, reject) {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};

module.exports = Promise;
